#include "t0022-0.h"
#include "cui.h"

#define MODNAME     "[CMEN]"
extern struct cui_widget_operations __menu_ops;
static void destroy_menu(struct cui_menu_st *cmenu);
static int create_menu(struct cui_widget_st *widget, int current_item_index);

int cui_menu_parse(cJSON *root, struct cui_widget_st* widget)
{
    int retval = -1;
    cJSON *node, *format, *item, *itemlist;
    // CJSON *actionlist;
    struct cui_menu_st *cmenu = zmalloc(sizeof(struct cui_menu_st));
    ERRSYS_INFOPRINT("### parsing MENU ...\n");
    // int nactionlist;
    if (cmenu == NULL) {
        goto err;
    }
    retval = cui_attributes_parse(root, widget, &__menu_ops, cmenu);
    if (retval == -1) {
        goto err;
    }

    // 设置widget类型
    SET_WIDGET_TYPE(widget, MENU);

    cui_dump_widget_hierarchy(widget);
    format = cJSON_GetObjectItem(root, CUI_CONFIELD_FORMAT);
    if (format != NULL) {
        node = cJSON_GetObjectItem(format, CUI_CONFIELD_MENUROWS);
        if (cJSON_ValidNumber(node)) {
            cmenu->mrows = node->valueint;
        }
        node = cJSON_GetObjectItem(format, CUI_CONFIELD_MENUCOLS);
        if (cJSON_ValidNumber(node)) {
            cmenu->mcols = node->valueint;
        }
    }
    node = cJSON_GetObjectItem(root, CUI_CONFIELD_MARK);
    if (cJSON_ValidString(node)) {
        strncpy(cmenu->mark, node->valuestring, CUI_WIDGETMARK_BUFLEN);
    }
    else {
        strncpy(cmenu->mark, " * ", CUI_WIDGETMARK_BUFLEN);
    }
    itemlist = cJSON_GetObjectItem(root, CUI_CONFIELD_ITEMLIST);
    if (cJSON_ValidArray(itemlist)) {
        int i;
        int nitems = cJSON_GetArraySize(itemlist);
        if (nitems > CUI_MENUITEMS_MAX) {
            ERRSYS_INFOPRINT("too many menu items (%d) > max(%d)\n", nitems, CUI_MENUITEMS_MAX);
        }
        else if (nitems == 0) {
            ERRSYS_INFOPRINT("menu items = 0\n");
        }
        else {
            ERRSYS_INFOPRINT("invalid menu items (%d)\n", nitems);
        }
        cmenu->nitems = nitems;
        // ERRSYS_INFOPRINT("cui_menu_parse menu items (%d)\n", nitems);
        for (i = 0;i < nitems;i ++) {
            cJSON *name, *desc;
            item = cJSON_GetArrayItem(itemlist, i);
            name = cJSON_GetObjectItem(item, CUI_CONFIELD_NAME);
            desc = cJSON_GetObjectItem(item, CUI_CONFIELD_DESC);
            if (name != NULL) {
                if (cJSON_ValidString(name)) {
                    int n = strlen(name->valuestring) + 1;
                    cmenu->name[i] = zmalloc(n);
                    if (cmenu->name[i] != NULL) {
                        strncpy(cmenu->name[i], name->valuestring, n);
                    }
                    if (desc != NULL) {
                        if (cJSON_ValidString(desc)) {
                            int n = strlen(desc->valuestring) + 1;
                            cmenu->desc[i] = zmalloc(n);
                            if (cmenu->desc[i] != NULL) {
                                strncpy(cmenu->desc[i], desc->valuestring, n);
                            }
                        }
                    }
                }
            }
        }
        // actionlist = cJSON_GetObjectItem(root, CUI_CONFIELD_ACTIONLIST);
        // if (cJSON_ValidArray(actionlist)) {
        //     nactionlist = cJSON_GetArraySize(actionlist);
        //     for (i = 0;i < nactionlist;i ++) {
        //         cJSON *action = cJSON_GetArrayItem(actionlist, i);
        //         cJSON *event = cJSON_GetObjectItem(action, CUI_CONFIELD_EVENT);
        //         cJSON *slot = cJSON_GetObjectItem(action, CUI_CONFIELD_SLOT);
        //     }
        // }
    }
    retval = 0;
err:
    if (retval != 0) {
        if (widget != NULL) {
            widget->ops.destroy(widget);
        }
    }
    return retval;
}

int cui_menu_mark(struct cui_widget_st *widget, const char *mark)
{
    int retval = -1;
    if (widget != NULL) {
        struct cui_menu_st *cmenu = widget->widgetinstance;
        set_menu_mark(cmenu->menu, mark);
        retval = 0;
    }

    return retval;
}

int cui_menu_initialize(struct cui_widget_st *widget, int nitems, const char **item_namelist, const char **item_desclist, int current_item_index)

{
    int retval = -1;
    if (widget != NULL) {
        int i;
        struct cui_menu_st *cmenu = widget->widgetinstance;
        destroy_menu(cmenu);
        for (i = 0;i < nitems && i < CUI_MENUITEMS_MAX;i ++) {
            if (item_namelist != NULL && item_namelist[i] != NULL) {
                int n = strlen(item_namelist[i]) + 1;
                cmenu->name[i] = zmalloc(n);
                if (cmenu->name[i] != NULL) {
                    strncpy(cmenu->name[i], item_namelist[i], n);
                }
                if (item_desclist != NULL && item_desclist[i] != NULL) {
                    if (item_desclist) {
                        int n = strlen(item_desclist[i]);
                        cmenu->desc[i] = zmalloc(n);
                        if (cmenu->desc[i] != NULL) {
                            strncpy(cmenu->desc[i], item_desclist[i], n);
                        }
                    }
                }
                ERRSYS_INFOPRINT("item: name=%s desc=%s\n", cmenu->name[i], cmenu->desc[i]);
                cmenu->nitems ++;
            }
        }
        ERRSYS_INFOPRINT("total %d items\n", cmenu->nitems);
        retval = create_menu(widget, current_item_index);
    }
    return retval;
}

// 调整menu条目相对widget的位置和大小
void cui_menu_resize(struct cui_widget_st *widget, int lines, int cols, int beginy, int beginx, int mrows, int mcols)
{
    ERRSYS_INFOPRINT("%s\n", __func__);
    if (widget && widget->widgetinstance) {
        struct cui_menu_st *cmenu = widget->widgetinstance;
        cmenu->lines = lines >= 0 ? lines : cmenu->lines;
        cmenu->cols = cols >= 0 ? cols : cmenu->cols;
        cmenu->beginy = beginy >= 0 ? beginy : cmenu->beginy;
        cmenu->beginx = beginx >= 0 ? beginx : cmenu->beginx;
        cmenu->mrows = mrows >= 0 ? mrows : cmenu->mrows;
        cmenu->mcols = mcols >= 0 ? mcols : cmenu->mcols;
        // ERRSYS_INFOPRINT("%s lines %d cols %d y %d x %d\n", __func__, cmenu->lines, cmenu->cols, cmenu->beginy, cmenu->beginx);
        set_menu_sub(cmenu->menu, derwin(widget->win, cmenu->lines, cmenu->cols, cmenu->beginy, cmenu->beginx));
        set_menu_format(cmenu->menu, cmenu->mrows, cmenu->mcols);
    }
}

void cui_menu_align(struct cui_widget_st *widget, int flags)
{
    ERRSYS_INFOPRINT("%s\n", __func__);
    if (widget) {
        struct cui_menu_st *cmenu = widget->widgetinstance;
        if (cmenu) {
            // 计算居中位置
            if (flags & CUI_WIDGET_ALIGNMENT_XMID) {
                cmenu->beginx = (widget->cols >> 1) - (cmenu->cols >> 1);
            }

            if (flags & CUI_WIDGET_ALIGNMENT_YMID) {
                cmenu->beginy = (widget->lines >> 1) - (cmenu->lines >> 1);
            }

            set_menu_sub(cmenu->menu, derwin(widget->win, cmenu->lines, cmenu->cols, cmenu->beginy, cmenu->beginx));
            // cui_dump_form(widget);
            // cui_dump_widget(widget);
        }
    }
}

static int create_menu(struct cui_widget_st *widget, int current_item_index)
{
    int retval = -1;
    struct cui_menu_st* cmenu = NULL;
    if (widget != NULL) {
        cmenu = widget->widgetinstance;
        if (cmenu) {
            WINDOW *win = widget->win;
            cmenu->items = (ITEM **)zmalloc(sizeof(ITEM *) * (cmenu->nitems + 1));
            ERRSYS_INFOPRINT("%s:[%s] total items: %d\n", __func__, widget->name, cmenu->nitems);
            if (cmenu->items != NULL) {
                int i;
                for(i = 0; i < cmenu->nitems; i ++) {
                    cmenu->items[i] = new_item(cmenu->name[i], cmenu->desc[i]);
                    ERRSYS_INFOPRINT("ITEM[%d] %s\n", i, cmenu->name[i]);
                }
                cmenu->items[i] = new_item(NULL, NULL);
                cmenu->menu = new_menu((ITEM **)cmenu->items);
                
                /* Set main window and sub window */
                set_menu_win(cmenu->menu, win);
                ERRSYS_INFOPRINT("MENU: \"%s\" x:%d y:%d lines:%d cols:%d\n", widget->name, widget->beginx, widget->beginy, widget->lines, widget->cols);
                // menu-rows/menu-cols set to default
                if (cmenu->mcols == 0 || cmenu->mrows == 0) {
                    if (IS_WIDGET_TITLE(widget)) {
                        cmenu->mcols = 1;
                        cmenu->mrows = widget->lines - 4;
                    }
                    else {
                        cmenu->mcols = 1;
                        cmenu->mrows = widget->lines - 2;
                    }

                }
                // to set derwin's beginx, beginy relative to cmenu->win, for menu list display
                if (IS_WIDGET_TITLE(widget)) {
                    cmenu->beginy = 3;
                    cmenu->beginx = 1;
                    cmenu->lines = widget->lines - 4;
                    cmenu->cols = widget->cols - 2;
                    // set_menu_sub(cmenu->menu, derwin(win, widget->lines - 4, widget->cols - 2, 3, 1));
                }
                else {
                    cmenu->beginy = 1;
                    cmenu->beginx = 1;
                    cmenu->lines = widget->lines - 2;
                    cmenu->cols = widget->cols - 2;
                    // set_menu_sub(cmenu->menu, derwin(win, widget->lines - 2, widget->cols - 2, 1, 1));
                }
                set_menu_sub(cmenu->menu, derwin(win, cmenu->lines, cmenu->cols, cmenu->beginy, cmenu->beginx));
                set_menu_format(cmenu->menu, cmenu->mrows, cmenu->mcols);
                /* Set menu mark to the string " * " */
                set_menu_mark(cmenu->menu, cmenu->mark);
                widget->flags |= CUI_WIDGET_FLAGS_INPUTEVENT;
                if (current_item_index != -1 && current_item_index < cmenu->nitems) {
                    set_current_item(cmenu->menu, cmenu->items[current_item_index]);
                }

                retval = 0;
            }
        }
    }
    return retval;
}

int cui_menu_create(struct cui_widget_st *widget)
{
    ERRSYS_INFOPRINT("[%s] create menu\n", widget->name);
    if (widget != NULL) {
        widget->win = newwin(widget->lines, widget->cols, widget->beginy, widget->beginx);
        return create_menu(widget, -1);
    }

    return -1;
}

void cui_menu_paint(struct cui_widget_st *widget)
{
    // ERRSYS_INFOPRINT("cui_menu_paint\n");
    if (widget != NULL) {
        struct cui_menu_st* cmenu = widget->widgetinstance;
        WINDOW *win = widget->win;
        int w, split_y;
        // int h;

        /* 2 lines/cols included for the border frame */
        w = widget->cols;
        // h = widget->lines;

        if (IS_WIDGET_BORDER(widget)) {
            // ERRSYS_INFOPRINT("[%s] paint cols:%d rows:%d\n", widget->name, w, h);
            box(win, 0, 0);
        }

        /* draw title */
        // ERRSYS_INFOPRINT("[%s]%s flags %x style %x\n", widget->name, __func__, widget->flags, widget->flags);
        if (IS_WIDGET_TITLE(widget)) {
            split_y = CUI_WIDGET_TITLESPLITTER_COLOFFSET;
            ERRSYS_INFOPRINT("[%s] paint title split line yoffset:%d xoffset:%d\n", widget->name, split_y, 0);
            mvwaddch(win, split_y, 0, ACS_LTEE);
            mvwhline(win, split_y, 1, ACS_HLINE, w - 2);
            mvwaddch(win, split_y, w - 1, ACS_RTEE);
            cui_print(win, split_y - 1, 1, w - 2, widget->title, WHTBLK, CUI_PRINT_ALIGNMENT_MIDDLE);
        }

        post_menu(cmenu->menu);
        wrefresh(win);
        // ERRSYS_INFOPRINT("[%s]%s cols:%d line:%d\n", widget->name, __func__, w, h);
    }
}

static void destroy_menu(struct cui_menu_st *cmenu)
{
    if (cmenu != NULL && cmenu->menu != NULL) {
        int i;
        // clean the terminal
        for(i = 0; i < cmenu->nitems; i ++) {
            if (cmenu->items[i] != NULL) {
                free_item(cmenu->items[i]);
            }
            if (cmenu->name[i] != NULL) {
                free(cmenu->name[i]);
            }
            if (cmenu->desc[i] != NULL) {
                free(cmenu->desc[i]);
            }
            cmenu->name[i] = NULL;
            cmenu->desc[i] = NULL;
            cmenu->items[i] = NULL;
            cmenu->nitems = 0;
        }
        unpost_menu(cmenu->menu);
        free_menu(cmenu->menu);
        cmenu->menu = NULL;
    }
}

// free widget inside
void cui_menu_destroy(struct cui_widget_st *widget)
{
    if (widget != NULL) {
        struct cui_menu_st* cmenu = widget->widgetinstance;
        ERRSYS_INFOPRINT("destroy widget (%s)\n", widget->name);
        destroy_menu(cmenu);
        cmenu->menu = NULL;
        delwin(widget->win);
        widget->win = NULL;
        widget->widgetinstance = NULL;
    }
}

int cui_menu_keyevent(struct cui_widget_st* widget, int key)
{
    int handled = CUI_WIDGETEVENT_PROPAGATION;

    if (widget != NULL) {
        struct cui_menu_st *cmenu = widget->widgetinstance;

        ERRSYS_INFOPRINT("%s:%s %s(%d) \n", widget->name, __func__, cui_key_str(key), key);

        if (!IS_WIDGET_KEYEVENTOFF(widget)) {
            /* default handler */
            switch(key) {
                case KEY_DOWN:
                    menu_driver(cmenu->menu, REQ_DOWN_ITEM);
                    break;
                case KEY_UP:
                    menu_driver(cmenu->menu, REQ_UP_ITEM);
                    break;
                case KEY_LEFT:
                    menu_driver(cmenu->menu, REQ_LEFT_ITEM);
                    break;
                case KEY_RIGHT:
                    menu_driver(cmenu->menu, REQ_RIGHT_ITEM);
                    break;
                case KEY_NPAGE:
                    menu_driver(cmenu->menu, REQ_SCR_DPAGE);
                    break;
                case KEY_PPAGE:
                    menu_driver(cmenu->menu, REQ_SCR_UPAGE);
                    break;
            }
            wrefresh(widget->win);
        }
        else {
            ERRSYS_INFOPRINT("%s:keyevent is turned off\n", widget->name);
        }
        
        /* customized handler */
        if (widget->cb.keyevent != NULL) {
            handled = widget->cb.keyevent(widget, key);
        }
    }

    return handled;
}

struct cui_widget_operations __menu_ops = {
    .name = "menu",
    .parse = cui_menu_parse,
    .create = cui_menu_create,
    .destroy = cui_menu_destroy,
    .paint = cui_menu_paint,
    .keyevent = cui_menu_keyevent,
};